﻿using System;
using System.Text;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Common.Eventing;
using Common.Eventing.InMemory;


namespace Common.Eventing.Tests
{
    [TestClass]
    public class the_inMemory_event_broker
    {
        private EventBroker _broker;


        [TestInitialize]
        public void BeforeEachTest()
        {
            _broker = new EventBroker();
        }


        [TestMethod]
        public void should_have_no_subscriptions_by_default()
        {
            Assert.IsFalse(_broker.Subscriptions.Any());
        }

        [TestMethod]
        public void should_have_one_subscription_when_one_is_added()
        {
            EventRegistration<int> handler = new EventRegistration<int>(
                delegate(int i)
                {
                    // nothing to do
                });

            _broker.Subscribe(handler);

            Assert.AreEqual<int>(1, _broker.Subscriptions.Count());
        }

        [TestMethod]
        public void should_have_zero_subscriptions_left_when_theOnly_one_isRemoved()
        {
            EventRegistration<int> handler = new EventRegistration<int>(
                delegate(int i)
                {
                    // nothing to do
                });

            _broker.Subscribe(handler);
            _broker.Unsubscribe(handler);

            Assert.AreEqual<int>(0, _broker.Subscriptions.Count());
        }

        [TestMethod]
        public void should_select_subscriptions_whose_handled_type_equals_theEvent_type()
        {
            EventRegistration<StreamWriter> handler = new EventRegistration<StreamWriter>(
                delegate(StreamWriter writer)
                {
                    // nothing to do
                });

            _broker.Subscribe(handler);

            Assert.AreEqual<int>(1, _broker.GetSubscriptionsFor(typeof(StreamWriter)).Count());
        }

        [TestMethod]
        public void should_select_subscriptions_whose_handled_type_isAssignable_from_theEvent_type()
        {
            // this will be our test event
            StreamWriter testEvent = new StreamWriter(new MemoryStream());

            // create a subscription for events of type TextWriter
            EventRegistration<TextWriter> handler = new EventRegistration<TextWriter>(
                delegate(TextWriter writer)
                {
                    // nothing to do
                });

            _broker.Subscribe(handler);

            Assert.IsTrue(handler.HandledEventType.IsAssignableFrom(testEvent.GetType()));
            Assert.AreEqual<int>(1, _broker.GetSubscriptionsFor(testEvent.GetType()).Count());
        }

        [TestMethod]
        public void should_give_published_events_to_allSelected_Subscriptions()
        {
            bool intHandlerRaised = false;
            bool textHandlerRaised = false;
            bool streamHandlerRaised = false;

            EventRegistration<int> intHandler = new EventRegistration<int>(
                delegate(int i)
                {
                    intHandlerRaised = true;
                });

            EventRegistration<StreamWriter> streamHandler = new EventRegistration<StreamWriter>(
                delegate(StreamWriter writer)
                {
                    streamHandlerRaised = true;
                });

            EventRegistration<TextWriter> textHandler = new EventRegistration<TextWriter>(
                delegate(TextWriter writer)
                {
                    textHandlerRaised = true;
                });

            _broker.Subscribe(intHandler);
            _broker.Subscribe(textHandler);
            _broker.Subscribe(streamHandler);

            _broker.Publish(new StreamWriter(new MemoryStream()));

            Assert.IsFalse(intHandlerRaised);
            Assert.IsTrue((textHandlerRaised && streamHandlerRaised));
        }

        [TestMethod]
        [ExpectedException(typeof(AggregateException))]
        public void should_throw_AggregateException_if_aSubscription_throws_anException()
        {
            EventRegistration<StreamWriter> streamHandler = new EventRegistration<StreamWriter>(
                delegate(StreamWriter writer)
                {
                    // nothing to do
                });

            EventRegistration<TextWriter> textHandler = new EventRegistration<TextWriter>(
                delegate(TextWriter writer)
                {
                    throw new Exception();
                });

            _broker.Subscribe(textHandler);
            _broker.Subscribe(streamHandler);

            _broker.Publish(new StreamWriter(new MemoryStream()));

            Assert.Fail("The Publish() method should have thrown an AggregateException");
        }

        [TestMethod]
        public void should_give_published_events_to_allSelected_subscriptions_evenIf_one_barfs()
        {
            bool intHandlerRaised = false;
            bool textHandlerRaised = false;
            bool streamHandlerRaised = false;

            EventRegistration<int> intHandler = new EventRegistration<int>(
                delegate(int i)
                {
                    intHandlerRaised = true;
                });

            EventRegistration<TextWriter> textHandler = new EventRegistration<TextWriter>(
                delegate(TextWriter writer)
                {
                    throw new Exception();
                });

            EventRegistration<StreamWriter> streamHandler = new EventRegistration<StreamWriter>(
                delegate(StreamWriter writer)
                {
                    streamHandlerRaised = true;
                });

            _broker.Subscribe(intHandler);
            _broker.Subscribe(textHandler);
            _broker.Subscribe(streamHandler);

            try
            {
                _broker.Publish(new StreamWriter(new MemoryStream()));
            }
            catch (AggregateException) { }

            Assert.IsFalse(intHandlerRaised);
            Assert.IsFalse(textHandlerRaised);
            Assert.IsTrue(streamHandlerRaised);
        }
    }
}
